package cn.iocoder.yudao.module.pay.service.packageorder;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.controller.app.packageorder.vo.AppPackageOrderPageReqVO;
import cn.iocoder.yudao.module.pay.controller.app.packageorder.vo.PackageOrderCreateReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.packagec.PackageDO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.mq.message.packageorder.PayPackageOrderSuccessMessage;
import cn.iocoder.yudao.module.pay.mq.producer.order.PayOrderProducer;
import cn.iocoder.yudao.module.pay.service.packagec.PackageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

import cn.iocoder.yudao.module.pay.controller.admin.packageorder.vo.*;
import cn.iocoder.yudao.module.pay.dal.dataobject.packageorder.PackageOrderDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.pay.dal.mysql.packageorder.PackageOrderMapper;

import static cn.hutool.core.util.ObjectUtil.notEqual;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;

/**
 * 套餐订单 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class PackageOrderServiceImpl implements PackageOrderService {

    /**
     * 使用得支付应用标识
     */
    private static final String PAY_APP_KEY = "interview_written_test";

    @Value("${yudao.pay.expire-time-hours}")
    private Long expireTimeHours;

    @Resource
    private PayOrderApi payOrderApi;
    @Resource
    private PackageOrderMapper packageOrderMapper;
    @Resource
    private PackageService packageService;
    @Resource
    private PayOrderProducer payOrderProducer;
    @Resource
    private MemberUserService memberUserService;

    @Override
    public Long createPackageOrder(Long userId, PackageOrderCreateReqVO createReqVO) {
        // 1.1 获得商品
        Long packageId = createReqVO.getPackageId();
        PackageDO packageDO = packageService.validPackage(packageId);
        Assert.notNull(packageDO, "套餐({}) 不存在", packageId);
        String packageName = packageDO.getPackageName();
        Integer price = packageDO.getSalePrice();

        // 1.2 插入 套餐 订单
        PackageOrderDO packageOrder = new PackageOrderDO().setUserId(userId)
                .setPackageId(packageDO.getId()).setPackageName(packageName)
                .setPrice(price).setPayStatus(false).setRefundPrice(0);
        packageOrderMapper.insert(packageOrder);

        // 2.1 创建支付单
        Long payOrderId = payOrderApi.createOrder(new PayOrderCreateReqDTO()
                .setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用
                .setMerchantOrderId(packageOrder.getId().toString()) // 业务的订单编号
                .setSubject(packageName).setBody("").setPrice(price) // 价格信息
                .setExpireTime(addTime(Duration.ofHours(expireTimeHours)))); // 支付的过期时间
        // 2.2 更新支付单到 package 订单
        packageOrderMapper.updateById(new PackageOrderDO().setId(packageOrder.getId())
                .setPayOrderId(payOrderId));
        // 返回支付单编号
        return payOrderId;
    }

    @Override
    public void updatePackageOrder(PackageOrderSaveReqVO updateReqVO) {
        // 校验存在
        validatePackageOrderExists(updateReqVO.getId());
        // 更新
        PackageOrderDO updateObj = BeanUtils.toBean(updateReqVO, PackageOrderDO.class);
        packageOrderMapper.updateById(updateObj);
    }

    @Override
    public void deletePackageOrder(Long id) {
        // 校验存在
        validatePackageOrderExists(id);
        // 删除
        packageOrderMapper.deleteById(id);
    }

    private void validatePackageOrderExists(Long id) {
        if (packageOrderMapper.selectById(id) == null) {
            throw exception(PACKAGE_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public PackageOrderDO getPackageOrder(Long id) {
        return packageOrderMapper.selectById(id);
    }

    @Override
    public PageResult<PackageOrderRespVO> getPackageOrderPage(PackageOrderPageReqVO pageReqVO) {
        PageResult<PackageOrderDO> pageResult = packageOrderMapper.selectPage(pageReqVO);
        PageResult<PackageOrderRespVO> orderRespVOPageResult = BeanUtils.toBean(pageResult, PackageOrderRespVO.class);
        orderRespVOPageResult.getList().forEach(order -> {
            Long userId = order.getUserId();
            MemberUserDO memberUserDO = memberUserService.getUser(userId);
            if (Objects.nonNull(memberUserDO)) {
                order.setNickname(memberUserDO.getNickname());
            }
        });
        return orderRespVOPageResult;
    }

    @Override
    public PageResult<PackageOrderDO> getPackageOrderPageForApp(AppPackageOrderPageReqVO pageReqVO) {
        return packageOrderMapper.selectPageForApp(pageReqVO);
    }

    /**
     * 校验支付订单的合法性
     *
     * @param order      交易订单
     * @param payOrderId 支付订单编号
     * @return 支付订单
     */
    private PayOrderRespDTO validatePayOrderPaid(PackageOrderDO order, Long payOrderId) {
        // 1. 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validatePayOrderPaid][order({}) payOrder({}) 不存在，请进行处理！]", order.getId(), payOrderId);
            throw exception(PAY_ORDER_NOT_FOUND);
        }
        // 2.1 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validatePayOrderPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    order.getId(), payOrderId, toJsonString(payOrder));
            throw exception(PACKAGE_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 2.1 校验支付金额一致
        if (notEqual(payOrder.getPrice(), order.getPrice())) {
            log.error("[validatePayOrderPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
                    order.getId(), payOrderId, toJsonString(order), toJsonString(payOrder));
            throw exception(PACKAGE_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
        }
        // 2.2 校验支付订单匹配（二次）
        if (notEqual(payOrder.getMerchantOrderId(), order.getId().toString())) {
            log.error("[validatePayOrderPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    order.getId(), payOrderId, toJsonString(payOrder));
            throw exception(PACKAGE_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }
        return payOrder;
    }

    @Override
    public void updatePackageOrderPaid(Long id, Long payOrderId) {
        // 1.1 校验订单是否存在
        PackageOrderDO order = packageOrderMapper.selectById(id);
        if (order == null) {
            log.error("[updatePackageOrderPaid][order({}) payOrder({}) 不存在订单，请进行处理！]", id, payOrderId);
            throw exception(PACKAGE_ORDER_NOT_FOUND);
        }
        // 1.2 校验订单已支付
        if (order.getPayStatus()) {
            // 特殊：支付单号相同，直接返回，说明重复回调
            if (ObjectUtil.equals(order.getPayOrderId(), payOrderId)) {
                log.warn("[updatePackageOrderPaid][order({}) 已支付，且支付单号相同({})，直接返回]", order, payOrderId);
                return;
            }
            // 异常：支付单号不同，说明支付单号错误
            log.error("[updatePackageOrderPaid][order({}) 支付单不匹配({})，请进行处理！]",
                    order, payOrderId);
            throw exception(PACKAGE_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }

        // 2. 校验支付订单的合法性
        PayOrderRespDTO payOrder = validatePayOrderPaid(order, payOrderId);

        // 3. 更新 PackageOrderDO 状态为已支付
        int updateCount = packageOrderMapper.updateByIdAndPayed(id, false,
                new PackageOrderDO().setPayStatus(true).setPayTime(LocalDateTime.now())
                        .setPayChannelCode(payOrder.getChannelCode()));
        if (updateCount == 0) {
            throw exception(PACKAGE_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
        }

        // 4. 发送支付成功消息
        payOrderProducer.sendPayOrderSuccessMessage(new PayPackageOrderSuccessMessage().setPayOrderId(payOrderId).setMerchantOrderId(id.toString()));
    }

    @Override
    public Integer totalPrice(Long userId) {
        Integer totalPrice = packageOrderMapper.totalPrice(userId);
        return Objects.isNull(totalPrice) ? 0 : totalPrice;
    }

}